<?php

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 * Description of NTB_Model
 *
 * @author Nguyen Thai Binh
 */
class NTB_Model extends CI_Model {

  //put your code here
  protected $table_name;
  protected $db_model = null;
  protected $default_order = null;
  protected $module;
  protected $class;
  protected $constraints;
  protected $schema;

  public function __construct(array $database_config = array(), $table_name = '') {
    parent::__construct();
    if (!isset($this->db_model))
      if (!empty($database_config)) {
        $database_config['autoinit'] = TRUE;
        $this->db_model = $this->load->database($database_config, TRUE);
      } else {
        $this->load->database();
        $this->db_model = $this->db;
      }
    if ($this->config->item('cache_db_on')) {
      $this->db_model->cache_on();
    } else if ($this->config->item('cache_all_db_on')) {
      $this->db_model->cache_on();
    }
    $this->class = get_called_class();
    $this->table_name = $this->config->item('prefix_db') . (empty($table_name) ? substr($this->class, 0, strpos($this->class, '_model')): $table_name);
    $this->module = CI::$APP->router->fetch_module();
    if (!empty($this->schema) && !$this->db_model->table_exists($this->table_name)) {
      $sql = explode(';', $this->schema);
      foreach ($sql as $s) {
        $s = trim($s);
        if (!empty($s)) {
          $this->db_model->query($s);
        }
      }
    }
  }

  protected function build_conditions($conditions) {
    if (!empty($conditions)) {
      if (is_string($conditions))
        $this->db_model->where($conditions);
      else
        foreach ($conditions as $key => $condition) {
          if ($key == 'or') {
            foreach ($condition as $k => $value)
              $this->db_model->or_where($k, $value);
          } else if ($key == 'in') {
            foreach ($condition as $k => $value)
              if (is_array($value))
                $this->db_model->where_in($k, $value);
              else
                $this->db_model->where_in($k, array($value));
          } else if ($key == 'not_in') {
            foreach ($condition as $k => $value)
              if (is_array($value))
                $this->db_model->where_not_in($k, $value);
              else
                $this->db_model->where_not_in($k, array($value));
          } else if ($key == 'or_in') {
            foreach ($condition as $k => $value)
              if (is_array($value))
                $this->db_model->or_where_in($k, $value);
              else
                $this->db_model->or_where_in($k, array($value));
          } else if ($key == 'or_not_in') {
            foreach ($condition as $k => $value)
              if (is_array($value))
                $this->db_model->or_where_not_in($k, $value);
              else
                $this->db_model->or_where_not_in($k, array($value));
          } else if ($key == 'like')
            foreach ($condition as $k => $value)
              $this->db_model->like($k, $value);
          else if ($key == 'or_like')
            foreach ($condition as $k => $value)
              $this->db_model->or_like($k, $value);
          else if ($key == 'not_like')
            foreach ($condition as $k => $value)
              $this->db_model->not_like($k, $value);
          else if ($key == 'or_not_like')
            foreach ($condition as $k => $value)
              $this->db_model->or_not_like($k, $value);
          else
            $this->db_model->where($key, $condition);
        }
    }
  }

  public function validate($obj) {
    $obj['validate_errors'] = array();
    if (!empty($this->constraints)) {
      $this->load->helper('valid');
      foreach ($this->constraints as $field => $conditions) {
        if (isset($obj[$field])) {
          foreach ($conditions as $kind => $value) {
            switch ($kind) {
              case 'blank':
                if ($obj[$field] == '' && $value === FALSE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.blank');
                break;
              case 'empty':
                if (empty($obj[$field]) && $value === FALSE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.empty');
                break;
              case 'unique':
                if (!empty($value)) {
                  $condition = array($field => $obj[$field]);
                  if (!empty($obj['id']))
                    $condition['id != '] = $obj['id'];
                  if (is_string($value)) {
                    if (isset($obj[$value]))
                      $condition[$value] = $obj[$value];
                  } else if (is_array($value)) {
                    foreach ($value as $f) {
                      if (isset($obj[$f]))
                        $condition[$f] = $obj[$f];
                    }
                  }
                  $object = $this->get_once($condition);
                  if (!empty($object))
                    $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.unique', $obj[$field]);
                }
                break;
              case 'email':
                $this->load->helper('email');
                if (!empty($obj[$field]) && !valid_email($obj[$field]) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.email.invalid');
                break;
              case 'is_numeric':
                if (!is_numeric($obj[$field]) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.is_numeric');
                break;
              case 'is_integer':
                if (!is_integer($obj[$field]) && (!(is_numeric($obj[$field]) && is_integer(intval($obj[$field])))) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.is_integer');
                break;
              case 'is_string':
                if (!is_string($obj[$field]) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.is_string');
                break;
              case 'url':
                if (!empty($obj[$field]) && !valid_url_format($obj[$field]) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.url.invalid');
                break;
              case 'credit_card':
                if (!empty($obj[$field]) && !valid_credit_card($obj[$field]) && $value === TRUE)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.credit_card.invalid');
                break;
              case 'nullable':
                if (is_null($obj[$field]) && !empty($value))
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.nullabled');
                break;
              case 'size':
                if (!empty($value)) {
                  $function = is_array($obj[$field]) ? 'count' : 'strlen';
                  if ((!is_array($value) && $function($obj[$field]) != $value) ||
                          (is_array($value) && (!isset($value[1]) && $function($obj[$field]) != $value) ||
                          (isset($value[1]) && $function($obj[$field]) > $value[1] && $function($obj[$field]) < $value[0] ) )) {
                    if (is_array($value) && count($value) > 1) {
                      $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.size.between', $value);
                    } else {
                      $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.size', is_array($value) ? end($value) : $value);
                    }
                  }
                }
                break;
              case 'in_list':
                if (is_array($value) && !in_array($obj[$field], $value))
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.inlist', implode(',', $value));
                break;
              case 'matches':
                if (!empty($value)) {
                  $value = is_array($value) ? $value : array($value);
                  foreach ($value as $regular_expression) {
                    if (!preg_match($regular_expression, $obj[$field])) {
                      $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name .
                              '.' . $field . '.validate' . '.matches', implode($regular_expression));
                      break;
                    }
                  }
                }
                break;
              case 'matche_field':
                if (!empty($value) && isset($obj[$value]) && $obj[$field] != $obj[$value])
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.matche_field', $value);
                break;
              case 'max':
                if (is_numeric($value) && $obj[$field] > $value)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.max', $value);
                break;
              case 'max_size':
                if (is_numeric($value) &&
                        ( ( is_array($obj[$field]) && count($obj[$field]) > $value) ||
                        ( is_string($obj[$field]) && strlen($obj[$field]) > $value ) ))
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.max_size', $value);
                break;
              case 'min':
                if (is_numeric($value) && $obj[$field] < $value)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.min', $value);
                break;
              case 'min_size':
                if (is_numeric($value) &&
                        ( ( is_array($obj[$field]) && count($obj[$field]) < $value) ||
                        ( is_string($obj[$field]) && strlen($obj[$field]) < $value ) ))
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.min_size', $value);
                break;
              case 'not_equal':
                if ($obj[$field] == $value)
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.not_equal', $value);
                break;
              case 'range':
                if (is_array($value) && count($value) > 1 && is_numeric($obj[$field]) && ($obj[$field] < $value[0] && $obj[$field] > $value[1]))
                  $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.range', $value);
                break;
              case 'validator':
                $this->load->helper('validator', $this->module);
                $value = is_array($value) ? $value : array($value);
                foreach ($value as $validator) {
                  if (function_exists($validator)) {
                    if (!$validator($obj[$field], $obj)) {
                      $obj['validate_errors'][$field][] = $this->lang->line($this->module . '.' . $this->table_name . '.' . $field . '.validate' . '.validator');
                      break;
                    }
                  }
                }
                break;
            }
          }
        }
      }
    }
    return $obj;
  }

  public function get_once($conditions = array()) {
    if (!is_array($conditions))
      $conditions = array('id' => $conditions);
    $this->build_conditions($conditions);
    $obj = $this->db_model->get($this->table_name, 1, 0)->result_array();
    if (isset($obj[0])) {
      return $obj[0];
    }
    return NULL;
  }

  public function get_list($conditions = array(), $max = 0, $offset = 0, $order = array(), $group_by = '', $distinct = false) {
    $this->build_conditions($conditions);
    if (!empty($max))
      $this->db_model->limit($max, $offset);
    $order = empty($order) ? $this->default_order : $order;
    if (!empty($order)) {
      if (is_array($order))
        foreach ($order as $key => $value)
          if (is_integer($key))
            $this->db_model->order_by($value, 'asc');
          else
            $this->db_model->order_by($key, $value);
      else
        $this->db_model->order_by($order, 'asc');
    } else {
      $this->db_model->order_by('id', 'asc');
    }
    if (!empty($group_by))
      $this->db_model->group_by($group_by);
    if (!empty($distinct)) {
      if (is_array($distinct))
        foreach ($distinct as $field)
          $this->db_model->select($field);
      else
        $this->db_model->select($distinct);
      $this->db_model->distinct();
    }
    return $this->db_model->get($this->table_name)->result_array();
  }

  public function insert($obj) {
    $obj = $this->validate($obj);
    if (empty($obj['validate_errors'])) {
      unset($obj['validate_errors']);
      $fields = $this->list_fields();
      if (!empty($fields)) {
        $temp_obj = array();
        foreach ($fields as $pro) {
          if (isset($obj[$pro]))
            $temp_obj[$pro] = $obj[$pro];
        }
      } else {
        $temp_obj = $obj;
      }
      $this->db_model->insert($this->table_name, $temp_obj);
      return $this->db_model->insert_id();
    } else
      return $obj;
  }

  public function save($obj) {
    $obj = $this->validate($obj);
    if (empty($obj['validate_errors'])) {
      unset($obj['validate_errors']);
      $fields = $this->list_fields();
      if (!empty($fields)) {
        $temp_obj = array();
        foreach ($fields as $pro) {
          if (isset($obj[$pro]))
            $temp_obj[$pro] = $obj[$pro];
        }
      } else {
        $temp_obj = $obj;
      }
      if (!empty($temp_obj['id'])) {
        $this->db_model->where('id', $temp_obj['id'])->update($this->table_name, $temp_obj);
        return true;
      } else {
        $this->db_model->insert($this->table_name, $temp_obj);
        return $this->db_model->insert_id();
      }
    } else
      return $obj;
  }

  public function update($conditions, $values) {
    if (empty($conditions) && empty($values))
      return false;
    if (!empty($conditions)) {
      if (is_array($conditions))
        $this->build_conditions($conditions);
      else
        $this->db_model->where('id', $conditions);
    }
    foreach ($values as $key => $value) {
      if (is_array($value) && count($value) >= 2) {
        $this->db_model->set($key, $value[0], $value[1]);
      } else
        $this->db_model->set($key, $value);
    }
    $this->db_model->update($this->table_name);
    return true;
  }

  public function delete($conditions) {
    if (empty($conditions))
      return false;
    if (is_array($conditions))
      $this->build_conditions($conditions);
    else
      $this->db_model->where('id', $conditions);
    return $this->db_model->delete($this->table_name);
  }

  public function truncate() {
    return $this->db_model->truncate($this->table_name);
  }

  public function total($conditions = array()) {
    $this->build_conditions($conditions);
    return $this->db_model->count_all_results($this->table_name);
  }

  public function list_fields($table = '') {
    if (empty($table))
      if (!empty($this->table_name))
        $table = $this->table_name;
    return $this->db_model->list_fields($table);
  }

  public function list_fields_data($table = '') {
    if (empty($table))
      if (!empty($this->table_name))
        $table = $this->table_name;
    return $this->db_model->field_data($table);
  }

  public function list_tables() {
    return $this->db_model->list_tables();
  }
  
  public function table_exists(){
    return $this->db_model->table_exists();
  }
  
  public function query($query) {
    return $this->db_model->query($query)->result_array();
  }

  public static function to_string($obj = array(), $default_field = 'id') {
    if (is_array($obj)) {
      if (isset($obj[$default_field]))
        return $obj[$default_field];
      if (isset($obj['id']))
        return $obj['id'];
      foreach ($obj as $key => $value)
        return $value;
    } else if (is_object($obj)) {
      if (isset($obj->$default_field))
        return $obj->$default_field;
      if (isset($obj->id))
        return $obj->id;
      $obj = get_object_vars($obj);
      foreach ($obj as $key => $value)
        return $value;
    } else
      return $obj . '';
  }

}